/*
 * TCSS 422 - Spring quarter 2011
 * Team: 	Googlers
 * Members: Deepa Sahni, Krisnil Gounder, Michael Schweiger
 * Date: 	April 6, 2011
 */
package web;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.htmlparser.Node;
import org.htmlparser.Parser;
import org.htmlparser.util.NodeIterator;

import buffers.URLBuffer;
import ui.Reporter;

/**
 * A parser designed to parse out hypertext links from html code and pass the links to
 * a PageBuffer object.
 * @author Michael Schweiger - API framework
 * @author Krisnil Gounder - Page Parser implementation.
 * @version 0.1
 */
public final class PageParser implements Runnable {

	/**
	 * The page being parsed by this parser.
	 */
	private String my_page = null;
	/**
	 * Pattern to be parsed if it is a href from the html source.
	 */
	private static final Pattern hrefMatch = Pattern.compile("href=[\'\"]?([^\'\" >]+)");
	/**
	 * Pattern to be parsed if it is a tag from the html source.
	 */
	private static final Pattern tagMatch = Pattern.compile("<[^>]*?>");
	/**
	 * Pattern to be parsed if it is a head from the html source.
	 */
	private static final Pattern headMatch = Pattern.compile("<head[^>]*>(.*?)</head>", Pattern.DOTALL);
	/**
	 * Pattern to be parsed if it is a script from the html source.
	 */
	private static final Pattern scriptMatch = Pattern.compile("<script[^>]*>(.*?)</script>");
	/**
	 * Pattern to be parsed if it is a word from the html source.
	 */
	private static final Pattern wordMatch = Pattern.compile("\\w*");
	/**
	 * Pattern to be parsed if it is a space from the html source.
	 */
	private static final Pattern spaceMatch = Pattern.compile("[\t\r\n]*");
	/**
	 * Pattern to be parsed if it is a ascii from the html source.
	 */
	private static final Pattern asciiMatch = Pattern.compile("&.*?;");
	
	/**
	 * The reporter object that will be passed to the gatherer object for display.
	 */
	//private final Reporter my_reporter
	
	
	/**
	 * Parsed data.
	 */
	private List<String> wordsFound = new ArrayList<String>();
	/**
	 * Parsed links.
	 */
	private List<String> linksFound = new ArrayList<String>();


	
	 /**
	  * PageParser constructor.
	  * 
	  * @param htmls, html from the Page Retriever.
	  * @param links, links that need to be sent back to the Page Retriever.
	  * @param parseResults, the parse output.
	  */
	PageParser(final BlockingQueue<URLBuffer> htmls, final BlockingQueue<String> links) {
		BlockingQueue<URLBuffer> _htmls = htmls;
		BlockingQueue<String> _links = links;

	}
	
	/**
	 * Constructor for this parser that takes a web page as a string.
	 * It will also parse the links and store it to the links Blocking Queue
	 * so that the Page Retriever can retrieve more html sites.
	 * 
	 * @param the_page The page as a string of text.  Cannot be null.
	 * @param the_reporter The reporter for displaying data, cannot be null.
	 * @throws IllegalArgumentException if params are null.
	 */
	public PageParser(String url, Reporter the_reporter, String the_page)
	throws IllegalArgumentException {
		
		if (the_page == null) {
			throw new IllegalArgumentException("Page string cannot be null!");
		
		//} else if (the_reporter == null) {
			//throw new IllegalArgumentException("The reporter cannot be null!");
			
		} else {
			the_page = removeMatch(the_page, headMatch, false);
			the_page = removeMatch(the_page, scriptMatch, false);
			Matcher hrefMatches = hrefMatch.matcher(the_page);
			
			// Identify links
			while (hrefMatches.find()) {
				String link = the_page.substring(hrefMatches.start(), hrefMatches.end());

				if (link.contains("javascript:") || link.contains("#")) {
					continue;
				}
				
				if (link.length() > 7) {
					if (link.startsWith("href=\"")) {
						link = link.substring(6, link.length());
					}
				}
				
				// url validation
				if (!url.startsWith("http")) {
				    url = "http://" + url;
				   }

				   URL baseURL = null;
				   URL linkURL = null;
				   try {
				    baseURL = new URL(url);
				    linkURL = new URL(baseURL, link);
				   } catch (MalformedURLException e) {
				    System.out.print("URL: " + url + " LINK: " + link);
				   }

				   link = linkURL.toString();
				   linksFound.add(link);
				  }
			
			

				if (!the_page.isEmpty()) {
					the_page = removeMatch(the_page, tagMatch, false);
					the_page = removeMatch(the_page, asciiMatch, true);
					the_page = removeMatch(the_page, spaceMatch, false);

					if (!the_page.isEmpty()) {
						Matcher wordMatches = wordMatch.matcher(the_page);

						while (wordMatches.find()) {
							String word = the_page.substring(wordMatches.start(), wordMatches.end());
							if (!word.startsWith(" ") && !word.equals("")) {
								wordsFound.add(word);
							}
						}
				}
			}
		}
		// need to pass data appropriately -->>>>
		return ;
	}
	
	
		
	/**
	 * This method removes the defined strings.
	 * 
	 * @param page
	 * @param the_p
	 * @param spaceAppend, reserve space for the match or not.
	 * @return page
	 */
	private String removeMatch(String page, final Pattern the_p, final boolean spaceAppend) {
		String[] temp = the_p.split(page);
		page = "";
		for (String s : temp) {
			page += s;
			if (spaceAppend) {
				page += ' ';
			}
		}
		return page;
	}

	
	/**
	 * Performs the parsing of the page.  Attempts to add any linked pages to PageBuffer
	 * as a URL as they are found.  Any content (not part of a markup tag) is returned as
	 * a new String.  Note that escape (&) characters are still in code form when returned.
	 * (IE. & will still be &amp;).  <b>NOTE: This method is not implemented yet</b>
	 * The same string that is returned is also given to a new Gatherer thread (when
	 * multithreading).
	 * @return The content of the page, after all html markup has been stripped from it.
	 */
	public String parse() {
		
		//delete after implementation
		return null;
	}

	/**
	 * Just starts the parse method.
	 */
	public void run() {
		parse();
	}
	
	
	/**
	 * toString for testing purpose.
	 */
	public void testOutput() {
		System.out.println("Words in the page: " + wordsFound);
		System.out.println("Links in the page: " + linksFound);
	}
}
